package com.retlife.tvshow.serial_port

import android.serialport.SerialPort
import android.util.Log
import java.io.BufferedInputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.Locale
import java.util.Queue
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit

/**
 * 串口实处理类
 */
class BaseSerialPoster(
    private val dataBits: Int,
    private val stopBits: Int,
    private val parity: Int,
    private val flags: Int
) : Runnable {
    private var path: String = "" //串口地址
    private var mSerialPort: SerialPort? = null //串口对象
    private var mInputStream: InputStream? = null //串口的输入流对象
    private var mBuffInputStream: BufferedInputStream? = null //用于监听硬件返回的信息
    private var mOutputStream: OutputStream? = null //串口的输出流对象 用于发送指令
    private var baseSerialReceiver: BaseSerialReceiver? = null //串口回调接口
    private var readTask: ScheduledFuture<*>? = null //串口读取任务
    private var sendStrTask: ScheduledFuture<*>? = null //循环发送任务
    private val scheduledExecutor //线程池 同一管理保证只有一个
            : ScheduledExecutorService
    private val queueMsg: Queue<ByteArray> = ConcurrentLinkedQueue() //线程安全到队列
    private var isSucc: Boolean = false

    init {
        scheduledExecutor = ScheduledThreadPoolExecutor(2) { r -> Thread(r, "BaseSerialHandle") }
    }

    fun startTest() {
//        scheduledExecutor.scheduleAtFixedRate({ send(CardCmdSupport.getBlockDataCmd(62)) }, 0, 15, TimeUnit.SECONDS)
    }

    /**
     * 添加串口回调
     *
     * @param baseSerialReceiver
     */
    fun addSerialInter(baseSerialReceiver: BaseSerialReceiver?) {
        this.baseSerialReceiver = baseSerialReceiver
    }

    /**
     * 打开串口
     *
     * @param devicePath 串口地址(根据平板的说明说填写)
     * @param baudrate   波特率(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param isRead     是否持续监听串口返回的数据
     * @return 是否打开成功
     */
    fun open(devicePath: String, baudrate: Int, isRead: Boolean): Boolean {
        return open(devicePath, baudrate, dataBits, stopBits, parity, isRead, flags)
    }

    /**
     * 打开串口
     *
     * @param devicePath 串口地址(根据平板的说明说填写)
     * @param baudrate   波特率(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param dataBits   数据位(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param stopBits   停止位(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param parity     校验位(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param isRead     是否持续监听串口返回的数据
     * @return 是否打开成功
     */
    private fun open(
        devicePath: String,
        baudrate: Int,
        dataBits: Int,
        stopBits: Int,
        parity: Int,
        isRead: Boolean,
        flags: Int
    ): Boolean {
        isSucc = false
        try {
            if (mSerialPort != null) {
                close()
            }
            val device = File(devicePath)
            mSerialPort = SerialPort // 串口对象
                .newBuilder(device, baudrate) // 串口地址地址，波特率
                .flags(flags)
                .dataBits(dataBits) // 数据位,默认8；可选值为5~8
                .stopBits(stopBits) // 停止位，默认1；1:1位停止位；2:2位停止位
                .parity(parity) // 校验位；0:无校验位(NONE，默认)；1:奇校验位(ODD);2:偶校验位(EVEN)
                .build() // 打开串口并返回
            mInputStream = mSerialPort?.inputStream
            mBuffInputStream = BufferedInputStream(mInputStream)
            mOutputStream = mSerialPort?.outputStream
            isSucc = true
            path = devicePath
            if (isRead) {
                readData() //开启识别
            }
        } catch (tr: Throwable) {
            close()
            isSucc = false
        }
        if (baseSerialReceiver != null) {
            baseSerialReceiver!!.connectMsg(path, isSucc)
        }
        return isSucc
    }

    // 读取数据
    private fun readData() {
        if (readTask != null) {
            readTask!!.cancel(true)
            try {
                Thread.sleep(160)
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
            //此处睡眠：当取消任务时 线程池已经执行任务，无法取消，所以等待线程池的任务执行完毕
            readTask = null
        }
        readTask = scheduledExecutor //获取线程池
            .schedule(this, 0, TimeUnit.MILLISECONDS) //执行一个循环任务
    }

    //每隔 150 毫秒会触发一次run
    override fun run() {
        while (isSucc) {
            try {
                Thread.sleep(10)
                if (Thread.currentThread().isInterrupted) {
                    continue
                }
                val available: Int = mBuffInputStream!!.available()
                if (available == 0) {
                    continue
                }
                val received = ByteArray(128)
                val size: Int = mBuffInputStream!!.read(received) //读取以下串口是否有新的数据
                if (size > 0 && baseSerialReceiver != null) {
                    baseSerialReceiver!!.readData(received, size)
                }
            } catch (e: IOException) {
                Log.e(TAG, "串口读取数据异常:$e")
            } catch (e: InterruptedException) {
                Log.e(TAG, "串口读取数据异常:$e")
            }
        }
    }

    /**
     * 关闭串口
     */
    fun close() {
        try {
            if (mInputStream != null) {
                mInputStream!!.close()
            }
        } catch (e: Exception) {
            Log.e(TAG, "串口输入流对象关闭异常：$e")
        }
        try {
            if (mOutputStream != null) {
                mOutputStream!!.close()
            }
        } catch (e: Exception) {
            Log.e(TAG, "串口输出流对象关闭异常：$e")
        }
        try {
            if (mSerialPort != null) {
                mSerialPort!!.close()
            }
            mSerialPort = null
        } catch (e: Exception) {
            Log.e(TAG, "串口对象关闭异常：$e")
        }
    }

    /**
     * 向串口发送指令
     */
    fun send(msg: ByteArray) {
        queueMsg.offer(msg) //向队列添加指令
    }

    //启动发送发送任务
    fun startSendTask() {
        cancelSendTask() //先检查是否已经启动了任务 ？ 若有则取消
        //每隔100毫秒检查一次 队列中是否有新的指令需要执行
        sendStrTask = scheduledExecutor.scheduleAtFixedRate(object : Runnable {
            override fun run() {
                if (!isSucc) {
                    return  //串口未连接 退出
                }
                val msg: ByteArray = queueMsg.poll()
                    ?: return  //无效指令 退出
                //取出指令
                //                byte[] bytes = hexStr2bytes(msg);//字符转成byte数组
                try {
                    Log.i("serial-port-send", path + "::" + bytes2hex(msg))
                    mOutputStream!!.write(msg) //通过输出流写入数据
                } catch (e: Exception) {
                    e.printStackTrace()
                } //发送指令
            }
        }, 0, 100, TimeUnit.MILLISECONDS)
    }

    //取消发送任务
    private fun cancelSendTask() {
        if (sendStrTask == null) {
            return
        }
        sendStrTask!!.cancel(true)
        try {
            Thread.sleep(100)
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
        sendStrTask = null
    }

    companion object {
        private const val TAG: String = "串口处理类"
        fun bytes2hex(bytes: ByteArray): String {
            val sb: StringBuilder = StringBuilder()
            var tmp: String?
            for (b: Byte in bytes) {
                // 将每个字节与0xFF进行与运算，然后转化为10进制，然后借助于Integer再转化为16进制
                tmp = Integer.toHexString(0xFF and b.toInt())
                if (tmp.length == 1) {
                    tmp = "0$tmp"
                }
                sb.append(tmp).append(" ")
            }
            return sb.toString()
        }

        /**
         * 把十六进制表示的字节数组字符串，转换成十六进制字节数组
         *
         * @param
         * @return byte[]
         */
        fun hexStr2bytes(hex: String): ByteArray {
            val len: Int = (hex.length / 2)
            val result = ByteArray(len)
            val achar: CharArray = hex.uppercase(Locale.getDefault()).toCharArray()
            for (i in 0 until len) {
                val pos: Int = i * 2
                result[i] =
                    ((hexChar2byte(achar[pos]) shl 4) or hexChar2byte(achar[pos + 1])).toByte()
            }
            return result
        }

        /**
         * 把16进制字符[0123456789abcde]（含大小写）转成字节
         *
         * @param c
         * @return
         */
        private fun hexChar2byte(c: Char): Int {
            when (c) {
                '0' -> return 0
                '1' -> return 1
                '2' -> return 2
                '3' -> return 3
                '4' -> return 4
                '5' -> return 5
                '6' -> return 6
                '7' -> return 7
                '8' -> return 8
                '9' -> return 9
                'a', 'A' -> return 10
                'b', 'B' -> return 11
                'c', 'C' -> return 12
                'd', 'D' -> return 13
                'e', 'E' -> return 14
                'f', 'F' -> return 15
                else -> return -1
            }
        }
    }
}